home *** CD-ROM | disk | FTP | other *** search
/ Internet Surfer: Getting Started / Internet Surfer - Getting Started (Wayzata Technology)(7231)(1995).bin / pc / mac / bonus / peter_le / chat__ / chat.p < prev   
Text File  |  1992-11-28  |  15KB  |  623 lines

  1. {$I-}
  2. program Chat;
  3.  
  4. { This program was written by Peter N Lewis, Mar 1992 in THINK Pascal 4.0.1 }
  5. { You may use this source in your own free/shareware projects as long as you acknowledge me }
  6. { in your About box and documentation files.  You may include it in commercial products }
  7. { only if I explicitly allow it. }
  8.  
  9.     uses
  10.         TCPStuff, TCPConnections, MyTypes, MyUtils, MyLists, MyStripTelnetCodes;
  11.  
  12.     const
  13.         globalStrhResID = 128;
  14.         channelStrhResID = 129;
  15.         commandStrhResID = 130;
  16.         max_channel = 10;
  17.         bad_rn = -1;
  18.  
  19.     type
  20.         strings = (noIndex, portIndex, irclogname, irclogtype, quitnowIndex, {}
  21.             howdullIndex, welcomeIndex, badChannelIndex, enternameIndex,{}
  22.             loggedinatIndex, youneedanameIndex, nameinuseIndex,{}
  23.             welcome2index1, welcomewarningIndex, welcome2index2, hasenteredIndex, {}
  24.             closingdownIndex, closingdownatIndex, helpIndex, helpIndex2,{}
  25.             byebyeIndex, colonIndex, hasleftIndex,{}
  26.             echoIndex, badparamIndex, badvariableIndex, {}
  27.             lastIndex);
  28.         commands = (C_None, C_Quit, C_List, C_Action1, C_Action2, C_Set);
  29.  
  30.     type
  31.         infoRecord = record
  32.                 cp: connectionIndex;
  33.                 tp: TCPConnectionPtr;
  34.                 state: (S_unconnected, S_GettingChannel, S_GettingName, S_GettingPassword, S_Connected, S_Closed);
  35.                 buffer: str255;
  36.                 name: str31;
  37.                 channel: str31;
  38.                 channel_index: integer;
  39.                 wason: boolean;
  40.                 echotoyou: boolean;
  41.                 requirequote: boolean;
  42.             end;
  43.         infoPtr = ^infoRecord;
  44.  
  45.     var
  46.         lh: listHead;
  47.         quitNow: boolean;
  48.         connected: integer;
  49.         port: integer;
  50.         dolog: boolean;
  51.         logrns: array[1..max_channel] of integer;
  52.  
  53.     function GetGlobalString (n: strings): str255;
  54.         var
  55.             s: str255;
  56.     begin
  57.         GetIndString(s, globalStrhResID, ord(n));
  58.         GetGlobalString := s;
  59.     end;
  60.  
  61.     procedure CreatePC;
  62.         var
  63.             p: infoPtr;
  64.             oe: OSErr;
  65.     begin
  66.         p := infoPtr(Newptr(SizeOf(infoRecord)));
  67.         p^.state := S_unconnected;
  68.         p^.channel := '';
  69.         p^.channel_index := 0;
  70.         p^.wason := false;
  71.         p^.echotoyou := false;
  72.         p^.requirequote := false;
  73.         oe := NewPassiveConnection(p^.cp, Minimum_TCPBUFFERSIZE, port, 0, 0, p);
  74.         AddTail(lh, p);
  75.     end;
  76.  
  77.     procedure DestroyPC (p: infoPtr);
  78.         var
  79.             item: listItem;
  80.             lp: infoPtr;
  81.     begin
  82.         if FindItem(lh, p, item) then begin
  83.             DisposPtr(ptr(p));
  84.             DeleteItem(item, p);
  85.         end;
  86.     end;
  87.  
  88.     procedure StartLog (var info: infoRecord; name: str255);
  89.         var
  90.             oe, ooe: OSErr;
  91.             logrn: integer;
  92.     begin
  93.         oe := HCreate(-1, 2, name, GetGlobalString(irclogtype), 'TEXT');
  94.         oe := HOpen(-1, 2, name, fsWrPerm, logrn);
  95.         if oe = noErr then begin
  96.             oe := SetFPos(logrn, fsFromLEOF, 0);
  97.             if oe <> noErr then
  98.                 ooe := FSClose(logrn);
  99.         end;
  100.         if oe = noErr then
  101.             logrns[info.channel_index] := logrn;
  102.     end;
  103.  
  104.     procedure StopLog (var info: infoRecord);
  105.         var
  106.             oe: OSErr;
  107.     begin
  108.         if logrns[info.channel_index] <> bad_rn then begin
  109.             oe := FSClose(logrns[info.channel_index]);
  110.             logrns[info.channel_index] := bad_rn;
  111.         end;
  112.     end;
  113.  
  114.     procedure StopAllLogs;
  115.         var
  116.             oe: OSErr;
  117.             i: integer;
  118.     begin
  119.         for i := 1 to max_channel do
  120.             if logrns[i] <> bad_rn then
  121.                 oe := FSClose(logrns[i]);
  122.     end;
  123.  
  124.     procedure Log (var info: infoRecord; s: str255);
  125.         var
  126.             count: longInt;
  127.             oe: OSErr;
  128.     begin
  129. {$PUSH}
  130. {$R-}
  131.         if s[length(s)] = lf then
  132.             s[0] := chr(ord(s[0]) - 1);
  133.         count := length(s);
  134.         oe := FSWrite(logrns[info.channel_index], count, @s[1]);
  135. {$POP}
  136.     end;
  137.  
  138.     function EnterChannel (var info: infoRecord): boolean;
  139.         var
  140.             i: integer;
  141.             s: str255;
  142.     begin
  143.         i := 1;
  144.         info.channel_index := 0;
  145.         GetIndString(s, channelStrhResID, i * 2 - 1);
  146.         while (i <= max_channel) & (s <> '') do begin
  147.             if IUEqualString(s, info.channel) = 0 then begin
  148.                 info.channel_index := i;
  149.                 if logrns[i] = bad_rn then begin
  150.                     GetIndString(s, channelStrhResID, i * 2);
  151.                     if s <> '' then begin
  152.                         StartLog(info, s);
  153.                     end;
  154.                 end;
  155.                 leave;
  156.             end;
  157.             i := i + 1;
  158.             GetIndString(s, channelStrhResID, i * 2 - 1);
  159.         end;
  160.         EnterChannel := info.channel_index > 0;
  161.     end;
  162.  
  163.     procedure LeaveChannel (var p: infoPtr);
  164.         var
  165.             item: listItem;
  166.             lp: infoPtr;
  167.             someoneelse: boolean;
  168.     begin
  169.         if p^.channel_index <> 0 then begin
  170.             someoneelse := false;
  171.             ReturnHead(lh, item);
  172.             while not IsTail(item) do begin
  173.                 Fetch(item, lp);
  174.                 if (lp <> p) & (lp^.channel_index = p^.channel_index) then begin
  175.                     someoneelse := true;
  176.                     leave;
  177.                 end;
  178.                 MoveToNext(item);
  179.             end;
  180.             if not someoneelse then
  181.                 StopLog(p^);
  182.         end;
  183.     end;
  184.  
  185.     function GetLine (tcpc: TCPConnectionPtr; value: longInt; var buffer: str255): boolean;
  186.         var
  187.             len: longInt;
  188.             gotlf: boolean;
  189.             i, j: integer;
  190.     begin
  191.         GetLine := false;
  192.         len := length(buffer);
  193. {$PUSH}
  194. {$R-}
  195.         if TCPReceiveUpTo(tcpc, 10, 1, @buffer[1], SizeOf(buffer) - 1, len, gotlf) = noErr then begin
  196.             i := 1;
  197.             j := 1;
  198.             while (i <= len) do begin
  199.                 case buffer[i] of
  200.                     cr, lf: 
  201.                         i := i + 1;
  202.                     bs, del:  begin
  203.                         i := i + 1;
  204.                         if j > 1 then
  205.                             j := j - 1;
  206.                     end;
  207.                     otherwise begin
  208.                         buffer[j] := buffer[i];
  209.                         i := i + 1;
  210.                         j := j + 1;
  211.                     end;
  212.                 end;
  213.             end;
  214.             buffer[0] := chr(j - 1);
  215.             GetLine := gotlf;
  216.         end;
  217. {$POP}
  218.     end;
  219.  
  220.     procedure SendString (tcpc: TCPCOnnectionPtr; s: str255);
  221.         var
  222.             oe: OSErr;
  223.     begin
  224. {$PUSH}
  225. {$R-}
  226.         oe := TCPSendAsync(tcpc, @s[1], length(s), true, nil);
  227. {$POP}
  228.     end;
  229.  
  230.     function OtherOnChannel (p, lp: infoPtr): boolean;
  231.     begin
  232.         OtherOnChannel := (lp <> p) & (lp^.state = S_connected) & (lp^.channel_index = p^.channel_index);
  233.     end;
  234.  
  235.     function WeakOtherOnChannel (p, lp: infoPtr): boolean;
  236.     begin
  237.         WeakOtherOnChannel := ((lp <> p) or (p^.echotoyou)) & (lp^.state = S_connected) & (lp^.channel_index = p^.channel_index);
  238.     end;
  239.  
  240.     procedure SendExceptString (p: infoPtr; s: str255);
  241.         var
  242.             item: listItem;
  243.             lp: infoPtr;
  244.             tcpc: TCPConnectionPtr;
  245.     begin
  246.         Log(p^, s);
  247.         ReturnHead(lh, item);
  248.         while not IsTail(item) do begin
  249.             Fetch(item, lp);
  250.             if WeakOtherOnChannel(p, lp) then begin
  251.                 GetConnectionTCPC(lp^.cp, tcpc);
  252.                 SendString(tcpc, s);
  253.             end;
  254.             MoveToNext(item);
  255.         end;
  256.     end;
  257.  
  258.     type
  259.         SEFormat = (SE_Speak, SE_Action, SE_Notice);
  260.  
  261.     procedure SendExceptNameString (p: infoPtr; s: str255; format: SEFormat);
  262.         var
  263.             colon: str15;
  264.             i, linelen: integer;
  265.             out: str255;
  266.     begin
  267.         case format of
  268.             SE_Speak: 
  269.                 colon := ': ';
  270.             SE_Action: 
  271.                 colon := ' ';
  272.             SE_Notice: 
  273.                 colon := ' ';
  274.         end;
  275.         linelen := 72 - length(colon) - length(p^.name);
  276.         for i := 1 to length(s) do
  277.             if s[i] = tab then
  278.                 s[i] := spc;
  279.         repeat
  280.             if length(s) > 78 - length(colon) - length(p^.name) then begin
  281.                 i := linelen;
  282.                 while (i > 0) and (s[i] <> spc) do begin
  283.                     i := i - 1;
  284.                 end;
  285.                 while (i > 0) and (s[i] = spc) do begin
  286.                     i := i - 1;
  287.                 end;
  288.                 if i < 1 then
  289.                     i := linelen;
  290.             end
  291.             else
  292.                 i := length(s);
  293.             out := concat(p^.name, colon, copy(s, 1, i));
  294.             if format = SE_Notice then
  295.                 out := concat('*', out, '*');
  296.             SendExceptString(p, concat(out, cr, lf));
  297.             i := i + 1;
  298.             while (i <= length(s)) and (s[i] = spc) do begin
  299.                 i := i + 1;
  300.             end;
  301.             s := copy(s, i, 255);
  302.         until s = '';
  303.     end;
  304.  
  305.     procedure FixName (var s: str31);
  306.         var
  307.             i: integer;
  308.     begin
  309.         for i := 1 to length(s) do
  310.             if not (s[i] in ['a'..'z', 'A'..'Z', '0'..'9', '-', '$', '/']) then
  311.                 s[i] := '_';
  312.     end;
  313.  
  314.     function NameInUse (p: infoPtr): boolean;
  315.         var
  316.             item: listItem;
  317.             lp: infoPtr;
  318.             tcpc: TCPConnectionPtr;
  319.     begin
  320.         NameInUse := false;
  321.         ReturnHead(lh, item);
  322.         while not IsTail(item) do begin
  323.             Fetch(item, lp);
  324.             if OtherOnChannel(p, lp) then begin
  325.                 if IUEqualString(lp^.name, p^.name) = 0 then begin
  326.                     NameInUse := true;
  327.                     leave;
  328.                 end;
  329.             end;
  330.             MoveToNext(item);
  331.         end;
  332.     end;
  333.  
  334.     procedure SendExceptNames (p: infoPtr);
  335.         var
  336.             item: listItem;
  337.             lp: infoPtr;
  338.             first: boolean;
  339.             len: integer;
  340.     begin
  341.         first := true;
  342.         len := 0;
  343.         ReturnHead(lh, item);
  344.         while not IsTail(item) do begin
  345.             Fetch(item, lp);
  346.             if OtherOnChannel(p, lp) then begin
  347.                 if first then
  348.                     first := false
  349.                 else begin
  350.                     SendString(p^.tp, ', ');
  351.                     len := len + 2;
  352.                 end;
  353.                 if len + length(lp^.name) > 75 then begin
  354.                     SendString(p^.tp, concat(cr, lf));
  355.                     len := 0;
  356.                 end;
  357.                 SendString(p^.tp, lp^.name);
  358.                 len := len + length(lp^.name);
  359.             end;
  360.             MoveToNext(item);
  361.         end;
  362.         if first then
  363.             SendString(p^.tp, concat(GetGlobalString(howdullIndex), cr, lf))
  364.         else
  365.             SendString(p^.tp, concat(cr, lf));
  366.     end;
  367.  
  368.     function GetTimeStr: str255;
  369.         var
  370.             st, sd: str255;
  371.             date: longInt;
  372.     begin
  373.         GetDateTime(date);
  374.         IUDateString(date, abbrevDate, sd);
  375.         IUTimeString(date, false, st);
  376.         GetTimeStr := concat(st, ', ', sd);
  377.     end;
  378.  
  379.     procedure GetWord (var line, word: str255);
  380.         var
  381.             p: integer;
  382.     begin
  383.         p := Pos(' ', line);
  384.         if p > 0 then begin
  385.             word := copy(line, 1, p - 1);
  386.             Delete(line, 1, p);
  387.         end
  388.         else begin
  389.             word := line;
  390.             line := '';
  391.         end;
  392.     end;
  393.  
  394.     function SetBoolean (var line: str255; var b: boolean): boolean;
  395.     begin
  396.         UpCaseString(line);
  397.         SetBoolean := false;
  398.         if line <> '' then begin
  399.             case line[1] of
  400.                 'Y', 'E', 'T':  begin
  401.                     b := true;
  402.                     SetBoolean := true;
  403.                 end;
  404.                 'N', 'D', 'F':  begin
  405.                     b := false;
  406.                     SetBoolean := true;
  407.                 end;
  408.                 'O':  begin
  409.                     if line = 'ON' then begin
  410.                         b := true;
  411.                         SetBoolean := true;
  412.                     end
  413.                     else if line = 'OFF' then begin
  414.                         b := false;
  415.                         SetBoolean := true;
  416.                     end;
  417.                 end;
  418.                 otherwise
  419.                     ;
  420.             end;
  421.         end;
  422.     end;
  423.  
  424.     procedure DoCommand (var p: infoPtr; line: str255);
  425.         var
  426.             ch: char;
  427.             i, ps: integer;
  428.             cmd: commands;
  429.             s, thecmd: str255;
  430.     begin
  431.         ch := nul;
  432.         if line <> '' then
  433.             ch := line[1];
  434.         case ch of
  435.             '/':  begin
  436.                 Delete(line, 1, 1);
  437.                 if line = GetGlobalString(quitnowIndex) then begin
  438.                     quitNow := true;
  439.                     SendString(p^.tp, concat(GetGlobalString(closingdownIndex), cr, lf));
  440.                     SendExceptString(p, concat(GetGlobalString(closingdownatIndex), GetTimeStr, cr, lf));
  441. { Should really send to everyone everywhere, but too much effort }
  442.                 end
  443.                 else begin
  444.                     GetWord(line, thecmd);
  445.                     i := 1;
  446.                     cmd := C_None;
  447.                     GetIndString(s, commandStrhResID, i * 2 - 1);
  448.                     while s <> '' do begin
  449.                         if IUEqualString(thecmd, s) = 0 then begin
  450.                             cmd := commands(i);
  451.                             leave;
  452.                         end;
  453.                         i := i + 1;
  454.                         GetIndString(s, commandStrhResID, i * 2 - 1);
  455.                     end;
  456.                     case cmd of
  457.                         C_Quit:  begin
  458.                             SendString(p^.tp, concat(GetGlobalString(byebyeIndex), cr, lf));
  459.                             p^.echotoyou := false;
  460.                             p^.state := S_Closed;
  461.                             CloseConnection(p^.cp);
  462.                         end;
  463.                         C_List:  begin
  464.                             SendExceptNames(p);
  465.                         end;
  466.                         C_Action1, C_Action2:  begin
  467.                             SendExceptNameString(p, line, SE_Action);
  468.                         end;
  469.                         C_Set:  begin
  470.                             GetWord(line, thecmd);
  471.                             if IUEqualString(thecmd, GetGlobalString(echoIndex)) = 0 then begin
  472.                                 if not SetBoolean(line, p^.echotoyou) then
  473.                                     SendString(p^.tp, concat(GetGlobalString(badparamIndex), cr, lf));
  474.                             end
  475.                             else begin
  476.                                 SendString(p^.tp, concat(GetGlobalString(badvariableIndex), cr, lf));
  477.                             end;
  478.                         end;
  479.                         otherwise begin
  480.                             SendString(p^.tp, concat(GetGlobalString(helpIndex), cr, lf));
  481.                             i := 1;
  482.                             GetIndString(s, commandStrhResID, i * 2);
  483.                             while s <> '' do begin
  484.                                 if s <> '<NONE>' then
  485.                                     SendString(p^.tp, concat(s, cr, lf));
  486.                                 i := i + 1;
  487.                                 GetIndString(s, commandStrhResID, i * 2);
  488.                             end;
  489.                             SendString(p^.tp, concat(GetGlobalString(helpIndex2), cr, lf));
  490.                         end;
  491.                     end;
  492.                 end;
  493.             end;
  494.             otherwise begin
  495.                 SendExceptNameString(p, line, SE_Speak);
  496.             end;
  497.         end;
  498.     end;
  499.  
  500.     procedure WNE;
  501.         var
  502.             dummy: boolean;
  503.             er: eventRecord;
  504.     begin
  505.         dummy := WaitNextEvent(everyEvent, er, 15, nil);
  506.         if er.what = keyDown then
  507.             quitNow := true;
  508.     end;
  509.  
  510.     function StackPtr: longInt;
  511.     inline
  512.         $2E8F;
  513.  
  514.     var
  515.         cer: connectionEventRecord;
  516.         p: infoPtr;
  517.         oe: OSErr;
  518.         dummylong: longInt;
  519.         i: integer;
  520.         last: str255;
  521. begin
  522.     SetApplLimit(ptr(StackPtr - 10000));
  523.     MaxApplZone;
  524.     MoreMasters;
  525.     if GetGlobalString(lastIndex) = '<LAST>' then begin
  526.         StringToNum(GetGlobalString(portIndex), dummylong);
  527.         port := dummylong;
  528.         for i := 1 to max_channel do
  529.             logrns[i] := bad_rn;
  530.  
  531.         if InitConnections = noErr then begin
  532.             CreateList(lh);
  533.             CreatePC;
  534.             CreatePC;
  535.             connected := 0;
  536.             while not quitNow do begin
  537.                 WNE;
  538.                 if GetConnectionEvent(any_connection, cer) then
  539.                     with cer do begin
  540.                         p := infoPtr(dataptr);
  541.                         with p^ do
  542.                             case event of
  543.                                 C_Established:  begin
  544.                                     connected := connected + 1;
  545.                                     state := S_GettingChannel;
  546.                                     buffer := '';
  547.                                     tp := tcpc;
  548.                                     SendString(tcpc, GetGlobalString(welcomeIndex));
  549.                                     CreatePC;
  550.                                 end;
  551.                                 C_CharsAvailable:  begin
  552.                                     if GetLine(tcpc, value, buffer) then begin
  553.                                         StripTelnetCodes(buffer);
  554.                                         case state of
  555.                                             S_GettingChannel:  begin
  556.                                                 channel := buffer;
  557.                                                 if EnterChannel(p^) then begin
  558.                                                     SendString(tcpc, GetGlobalString(enternameIndex));
  559.                                                     state := S_GettingName;
  560.                                                 end
  561.                                                 else begin
  562.                                                     SendString(tcpc, concat(GetGlobalString(badChannelIndex), cr, lf));
  563.                                                     state := S_Closed;
  564.                                                     CloseConnection(connection);
  565.                                                 end;
  566.                                             end;
  567.                                             S_GettingName:  begin
  568.                                                 Log(p^, concat(buffer, GetGlobalString(loggedinatIndex), GetTimeStr, cr, lf));
  569.                                                 name := buffer;
  570.                                                 FixName(name);
  571.                                                 if name = '' then begin
  572.                                                     SendString(tcpc, concat(GetGlobalString(youneedanameIndex), cr, lf));
  573.                                                     state := S_Closed;
  574.                                                     CloseConnection(connection);
  575.                                                 end
  576.                                                 else if NameInUse(p) then begin
  577.                                                     SendString(tcpc, concat(GetGlobalString(nameinuseIndex), cr, lf));
  578.                                                     state := S_Closed;
  579.                                                     CloseConnection(connection);
  580.                                                 end
  581.                                                 else begin
  582.                                                     SendString(tcpc, GetGlobalString(welcome2index1));
  583.                                                     if logrns[channel_index] <> bad_rn then
  584.                                                         SendString(tcpc, GetGlobalString(welcomewarningIndex));
  585.                                                     SendString(tcpc, GetGlobalString(welcome2index2));
  586.                                                     state := S_connected;
  587.                                                     SendExceptNames(p);
  588.                                                     SendExceptNameString(p, GetGlobalString(hasenteredIndex), SE_Notice);
  589.                                                     wason := true;
  590.                                                 end;
  591.                                             end;
  592.                                             S_GettingPassword:  begin
  593.                                             end;
  594.                                             S_Connected:  begin
  595.                                                 DoCommand(p, buffer);
  596.                                             end;
  597.                                             otherwise
  598.                                                 ;
  599.                                         end;{case}
  600.                                         buffer := '';
  601.                                     end;{if getline}
  602.                                     StripTelnetCodes(buffer);
  603.                                 end;
  604.                                 C_Closing:  begin
  605.                                     state := S_Closed;
  606.                                     CloseConnection(connection);
  607.                                 end;
  608.                                 C_Closed:  begin
  609.                                     if wason then
  610.                                         SendExceptNameString(p, GetGlobalString(hasleftIndex), SE_Notice);
  611.                                     if channel_index > 0 then
  612.                                         LeaveChannel(p);
  613.                                     connected := connected - 1;
  614.                                     DestroyPC(p);
  615.                                 end;
  616.                             end;
  617.                     end;
  618.             end;
  619.             FinishEverything;
  620.         end;
  621.     end;
  622.     StopAllLogs;
  623. end.